package com.lry.client;

import com.lry.core.RpcCallback;
import com.lry.proto.ClassInfo;
import io.netty.channel.Channel;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/9/16
 */
public class RpcProxy implements MethodInterceptor {

    private RpcClient rpcClient;
    private Class clazz;

    public RpcProxy(RpcClient rpcClient, Class clazz) {
        this.rpcClient = rpcClient;
        this.clazz = clazz;
    }

    public static <T>T getProxy(RpcClient rpcClient, final Class clazz){
        Enhancer en = new Enhancer();
        en.setSuperclass(clazz);
        en.setCallback(new RpcProxy(rpcClient, clazz));

        return (T) en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        ClassInfo classInfo = new ClassInfo();

        classInfo.setMethodName(method.getName());
        RpcCallback rpcCallback = null;
        if(args[args.length-1] instanceof RpcCallback){
            rpcCallback = (RpcCallback) args[args.length-1];
        }

        //同步流程
        if(null==rpcCallback){
            classInfo.setClassName(clazz.getName());
            classInfo.setParamsTypeArr(method.getParameterTypes());
            classInfo.setParamsArr(args);
            return rpcClient.invokeRpc(classInfo);
        }
        //异步流程
        else{
            classInfo.setClassName(clazz.getInterfaces()[0].getName());
            classInfo.setParamsTypeArr(classes(method.getParameterTypes()));
            classInfo.setParamsArr(objs(args));

            RpcCallback finalRpcCallback = rpcCallback;
            Future<Object> future = CompletableFuture.supplyAsync(()->{
                Object obj = null;
                try{
                    obj = rpcClient.invokeRpc(classInfo);
                    finalRpcCallback.success(obj);
                }catch (Exception e){
                    finalRpcCallback.fail(e);
                }
                return obj;
            });
            return future;
        }
    }

    public Class[] classes(Class[] objs){
        if(objs.length<=1){
            return new Class[0];
        }
        Class[] robjs = new Class[objs.length-1];

        for(int i=0;i<objs.length-1;i++){
            robjs[i] = objs[i];
        }
        return robjs;
    }

    public Object[] objs(Object[] objs){
        if(objs.length<=1){
            return new Object[0];
        }
        Object[] robjs = new Object[objs.length-1];

        for(int i=0;i<objs.length-1;i++){
            robjs[i] = objs[i];
        }
        return robjs;
    }
}
